//{=================================================================================
//! @file    stack.h
//! @date    2013-11-15 22:01:24 +0400
//! @author  Roman Anufriev <mrspeedstalker@gmail.com>
//!
//! @brief   Header file that allows you to work with the stack.
//!
//!          It contains all necessary instruments to correctly work with the
//!          stack. Also it has some simple arithmetic functions.
//}=================================================================================

// Def checks was `"stack.h"` included before or not.
#ifndef __STACK_H_INCLUDED
#define __STACK_H_INCLUDED

#include <stdio.h>

//! Stack size.
const int STK_SZ = 10;

//! Const that have very bad value (especially for stack counter...).
const int POISON = -666;

//{=================================================================================
//! @brief   Checks is `type` OK or not.
//!
//!          Quite complicated define that checks if `type` OK and if not calls:
//!              * Function `"type"_OK`
//!              * Function `"type"_dump`
//!              * Assert that
//!                  + always wrong
//!                  + writes `"not_OK -> it is bad"`
//}=================================================================================
#define ASSERT_OK(type) if (!type##_OK(this_))               \
							{					             \
							type##_dump(this_),              \
							assert (!"not_OK -> it is bad"); \
							}

//! Set type of elem_t.
typedef double elem_t;

//{=================================================================================
//! @brief   Creates stack.
//! 
//!          Stack is consists of integer type variable `count` and static array
//!          of `STK_SZ` elements that have `elem_t` type.
//}=================================================================================
struct Stack_t
{
	int count;              //!< Shows how many elements in the stack.
	elem_t data[STK_SZ];    //!< The array where elements of the stack is stored.
};


// prototypes of functions
void Stack_construct (Stack_t* st);
void Stack_destruct  (Stack_t* st);

int  Stack_OK   (const Stack_t* st);
void Stack_dump (const Stack_t* st);

int    Stack_push (Stack_t* this_, elem_t val);
elem_t Stack_pop  (Stack_t* this_            );

int Stack_sum (Stack_t* this_);
int Stack_sub (Stack_t* this_);
int Stack_mul (Stack_t* this_);
int Stack_div (Stack_t* this_);


// functions themselves

				/*--------======== Main stack functions ========--------*/

//{=================================================================================
//! `Stack_construct` - constructs good stack. 
//!
//! @param     *st  address of the stack.
//!
//! @return    Address of the constructed stack. 
//!
//! @note      If (st == NULL) function printf:
//!                "NULL pointer to the stack. The stack did not constructed.\n".
//}=================================================================================
void Stack_construct (Stack_t* st)
	{
	if (!st)
		{
		printf ("NULL pointer to the stack. The stack did not constructed.\n");
		return;
		}

	st -> count = 0;
	};

//{============================================================================
//! `Stack_destruct` - destructs the stack.
//!
//! @param     *st  address of the stack.
//!
//! @return    Adress of destructed stack.
//!
//! @note      If (st == NULL) function printf:
//!                "NULL pointer to the stack that is being destructed.\n".
//! @note      If function could destruct stack it printf:
//!                "The stack is successfully destructed.\n"
//! @note      if not:
//!                 "The stack can not be destructed.\n".
//}============================================================================
void Stack_destruct (Stack_t* st)
	{
	if (!st)
		{
		printf ("NULL pointer to the stack that is being destructed.\n");
		return;
		}

	st -> count = POISON;
	for (int i = 0; i < STK_SZ; i++)
		{
		st -> data[i] = POISON;
		}
	
	if (Stack_OK (st) == 0)
		printf ("The stack is successfully destructed.\n");
	else
		{
		printf ("The stack can not be destructed.\n");
		Stack_dump (st);
		}
	}

//{============================================================================
//! `Stack_OK` - checks is the stack OK or not.
//!
//! @param     *st  address of the stack.
//!
//! @return    1    if stack is OK.
//! @return    0    if          not.
//}============================================================================
int Stack_OK (const Stack_t* st)
	{
	return (st && 0 <= (st -> count) && (st -> count) <= STK_SZ);
	}

//{============================================================================
//! `Stack_dump` - do the comlete dump of the stack.
//!
//! @param     *st  address of the stack.
//!
//! @note      Dump includes:
//!                * is stack OK or not
//!                * address of the beginning of the stack
//!                * content of the stack
//!                    + counter
//!                    + stack size
//!                    + each element of the stack
//!                         - used elements marked with '*'
//!                         - elements that used to be used are likely have
//!                           'POISON' value.
//}============================================================================
void Stack_dump (const Stack_t* st)
	{
	printf ("Dump of the stack:\n");
	if (Stack_OK (st) == 1)
		printf ("  the stack is OK;\n" );
	else
		printf ("  the stack is BAD;\n");
	printf ("  the address of the beginning of the stack: %x;\n", st);
	
	printf ("\t{\n"                                      );
	printf ("\tcount = %d\n",                 st -> count);
	printf ("\tSTK_SZ = %d\n\n",                   STK_SZ);

	for (int i = 0; i < STK_SZ; i++)
		{
		printf ("\tdata[%d] = %f ;",  i, st -> data[i] );
		
		if (i < (st -> count) )
			printf (              " *\n"               );
		else
			printf (              " ;\n"               );
		}
			
	printf("\t}\n"                                       );
	}

//{============================================================================
//! `Stack_push` - push 'elem_t' number in the stack.
//!
//! @param     *this_  address of the stack.
//! @param     val  element that should be pushed
//!
//! @return    1    element was successfully pushed, all is alright.
//! @return    0    stack is full, element could not be pushed.
//}============================================================================
int Stack_push (Stack_t* this_, elem_t val)
	{
	ASSERT_OK(Stack);
	
	if ( (this_ -> count) + 1 > STK_SZ)
		return 0;

	(this_ -> data[this_ -> count]) = val;
	(this_ -> count) ++;

	ASSERT_OK(Stack);
	return 1;
	}

//{============================================================================
//! `Stack_pop` - pops element from the top of the stack.
//!
//! @param        *this_     address of the stack.
//!
//! @return       pop_val    popped number
//!
//! @note         function prints "The stack is empty. You can not pop from an
//!                                empty stack.\n"
//!               if the stack is empty.
//}============================================================================
elem_t Stack_pop (Stack_t* this_)
	{
	elem_t pop_val = 0;
	ASSERT_OK(Stack);

	if ( (this_ -> count) < 1)
		{
		printf ("The stack is empty. You can not pop from an empty stack.\n");
		return POISON;
		}

	(this_ -> count) --;
	pop_val = (this_ -> data[this_ -> count]);
	this_ -> data[this_ -> count] = POISON;

	ASSERT_OK(Stack);
	return pop_val;
	}

				/*--------======== Arithmetic functions ========--------*/

//{============================================================================
//! `Stack_sum` - pops two elements from the top of the stack, sums them and
//!               push the result into the stack.
//!
//! @param     *this_    address of the stack.
//!
//! @return    1    function ended successfully, all is alright.
//! @return    0    number of elements in the stack are less than 2.
//}============================================================================
int Stack_sum (Stack_t* this_)
	{
	ASSERT_OK(Stack);
	elem_t x = 0, y = 0;
	int assert_ver_val = 0;

	if ( (this_ -> count) < 2)
		return 0;

	x = Stack_pop (this_);
	y = Stack_pop (this_);

	assert_ver_val = Stack_push (this_, y + x);
	assert (assert_ver_val);

	ASSERT_OK(Stack);
	return 1;
	}

//{============================================================================
//! `Stack_sub` - pops two elements from the top of the stack, subs them and
//!               push the result into the stack.
//!
//! @param     *this_    address of the stack.
//!
//! @return    1    function ended successfully, all is alright.
//! @return    0    number of elements in the stack are less than 2.
//!
//! @note      function subtracts the TOP placed number FROM the DEEPER
//!            (the first after the top) one.
//}============================================================================
int Stack_sub (Stack_t* this_)
	{
	ASSERT_OK(Stack);
	elem_t x = 0, y = 0;
	int assert_ver_val = 0;

	if ( (this_ -> count) < 2)
		return 0;

	x = Stack_pop (this_);
	y = Stack_pop (this_);

	assert_ver_val = Stack_push (this_, y - x);
	assert (assert_ver_val);

	ASSERT_OK(Stack);
	return 1;
	}

//{============================================================================
//! `Stack_mul` - pops two elements from the top of the stack, muls them and
//!               push the result into the stack.
//!
//! @param     *this_    address of the stack.
//!
//! @return    1    function ended successfully, all is alright.
//! @return    0    number of elements in the stack are less than 2.
//}============================================================================
int Stack_mul (Stack_t* this_)
	{
	ASSERT_OK(Stack);
	elem_t x = 0, y = 0;
	int assert_ver_val = 0;

	if ( (this_ -> count) < 2)
		return 0;

	x = Stack_pop (this_);
	y = Stack_pop (this_);

	assert_ver_val = Stack_push (this_, y * x);
	assert (assert_ver_val);

	ASSERT_OK(Stack);
	return 1;
	}

//{============================================================================
//! `Stack_div` - pops two elements from the top of the stack, divs them and
//!               push the result into the stack.
//!
//! @param     *this_    address of the stack.
//!
//! @return    1    function ended successfully, all is alright.
//! @return    0    number of elements in the stack are less than 2.
//!
//! @note      function divides the DEEPER (the first after the top) placed
//!            number BY the TOP one.
//}============================================================================
int Stack_div (Stack_t* this_)
	{
	ASSERT_OK(Stack);
	elem_t x = 0, y = 0;
	int assert_ver_val = 0;

	if ( (this_ -> count) < 2)
		return 0;

	x = Stack_pop (this_);
	y = Stack_pop (this_);

	if (x == 0)
		return 0;

	assert_ver_val = Stack_push (this_, y / x);
	assert (assert_ver_val);

	ASSERT_OK(Stack);
	return 1;
	}

#endif